#include "GitInfo.h"
#include <algorithm>

// 构造函数实现
// 参数：window - 要显示的窗口指针
//       dir - 要显示的目录路径，默认为当前目录
GitInfo::GitInfo(WINDOW *window, const std::string &dir)
    : win(window),          // 初始化窗口指针
      current_dir(dir),     // 初始化当前目录
      scroll_pos(0),        // 初始化滚动位置为0
      show_commit_input(false), // 初始化不显示提交输入框
      commit_message(""),   // 初始化提交信息为空字符串
      cursor_pos(0),        // 初始化光标位置为0
      has_input_focus(true), // 初始化输入框有焦点
      input_start_y(0),     // 初始化输入框Y坐标为0
      input_start_x(0),     // 初始化输入框X坐标为0
      input_lines(0)        // 初始化输入框行数为0
{
    // 启用窗口的特殊键输入功能（如方向键）
    keypad(win, TRUE);
    // 隐藏光标（0表示隐藏）
    curs_set(0);
    // 读取目录内容
    read_directory();
}

void GitInfo::calculate_cursor_position(int &y, int &x)
{
    // 获取窗口的宽度（减去4是为了留出边框和边距）
    int text_width = getmaxx(win) - 4;

    // 初始化光标位置为输入区域的起始位置
    y = input_start_y;
    x = input_start_x;

    // 遍历提交信息字符串，直到当前光标位置
    for (int i = 0; i < cursor_pos; ++i)
    {
        // 如果遇到换行符或者光标超出窗口宽度
        if (commit_message[i] == '\n' || x >= input_start_x + text_width - 1)
        {
            // 换行：y坐标增加1行
            y++;
            // x坐标回到起始位置
            x = input_start_x;

            // 如果不是换行符导致的换行（即自动换行情况）
            if (commit_message[i] != '\n')
            {
                // 自动换行时需要将x坐标右移1格
                x++;
            }
        }
        else
        {
            // 普通字符：x坐标右移1格
            x++;
        }
    }
}

void GitInfo::read_directory()
{
    items.clear(); // 清空当前文件列表

    DIR *dir_ptr;
    struct dirent *dir_entry;

    // 尝试打开目录
    if ((dir_ptr = opendir(current_dir.c_str())) != nullptr)
    {
        // 成功打开目录，开始读取内容
        while ((dir_entry = readdir(dir_ptr)) != nullptr)
        {
            std::string file_name = dir_entry->d_name;

            // 跳过 "." 和 ".." 这两个特殊目录
            if (file_name != "." && file_name != "..")
            {
                items.push_back(file_name); // 添加到文件列表
            }
        }
        closedir(dir_ptr); // 关闭目录

        // 按文件名排序（字母顺序）
        std::sort(items.begin(), items.end(), [](const std::string &a, const std::string &b)
                  { return a < b; });
    }
    else
    {
        // 目录打开失败，添加错误信息
        items.push_back("无法打开目录: " + current_dir);
    }
}

void GitInfo::draw_window()
{
    // 清除窗口内容
    werase(win);
    // 绘制窗口边框
    box(win, 0, 0);

    // 获取窗口高度和宽度
    int win_height = getmaxy(win);
    int win_width = getmaxx(win);

    // 判断是否显示提交输入界面
    if (!show_commit_input)
    {
        // 计算内容区域高度（窗口高度减去标题和底部按钮空间）
        int content_height = win_height - 3;
        // 计算最大可显示项目数（内容高度减去上下边框）
        int max_items = content_height - 2;

        // 计算当前滚动位置和结束位置
        int start = scroll_pos;
        // 确保结束位置不超过项目总数
        int end = std::min(scroll_pos + max_items, static_cast<int>(items.size()));

        // 遍历当前可见的项目
        for (int i = start; i < end; ++i)
        {
            // 获取项目名称
            std::string display_name = items[i];
            // 如果名称太长，截断并添加省略号
            if (display_name.length() > static_cast<size_t>(win_width - 4))
            {
                display_name = display_name.substr(0, win_width - 7) + "...";
            }
            // 在窗口上打印项目名称（位置根据滚动位置调整）
            mvwprintw(win, i - scroll_pos + 1, 1, "%s", display_name.c_str());
        }

        // 如果上方还有内容，显示向上箭头
        if (scroll_pos > 0)
        {
            mvwaddch(win, 0, win_width - 2, ACS_UARROW);
        }
        // 如果下方还有内容，显示向下箭头
        if (scroll_pos + max_items < static_cast<int>(items.size()))
        {
            mvwaddch(win, content_height - 1, win_width - 2, ACS_DARROW);
        }

        // 显示窗口标题（包含当前目录）
        mvwprintw(win, 0, 2, "[ Git Add - %s ]", current_dir.c_str());

        // 如果当前没有输入焦点，反色显示"Next"按钮
        if (!has_input_focus)
            wattron(win, A_REVERSE);
        mvwprintw(win, win_height - 2, win_width - 10, "[ Next ]");
        if (!has_input_focus)
            wattroff(win, A_REVERSE);
    }
    else
    {
        // 显示提交界面的标题
        mvwprintw(win, 0, 2, "[ Git Commit ]");
        // 加粗显示提示文字
        wattron(win, A_BOLD);
        mvwprintw(win, 2, 1, "Commit Message (Enter for newline):");
        wattroff(win, A_BOLD);

        // 设置输入区域起始位置和大小
        input_start_y = 4;
        input_start_x = 2;
        input_lines = win_height - 6;
        int text_width = win_width - 4;

        // 分行处理提交信息
        int line = 0;
        int pos = 0;
        while (line < input_lines && pos < commit_message.length())
        {
            // 查找每行的起始和结束位置
            int start = pos;
            while (pos < commit_message.length() &&
                   pos - start < text_width &&
                   commit_message[pos] != '\n')
            {
                pos++;
            }

            // 获取当前行文本
            std::string line_text = commit_message.substr(start, pos - start);
            // 打印当前行
            mvwprintw(win, input_start_y + line, input_start_x, "%s", line_text.c_str());

            // 如果行文本不足行宽，用空格填充剩余部分
            if (line_text.length() < text_width)
            {
                mvwhline(win, input_start_y + line, input_start_x + line_text.length(),
                         ' ', text_width - line_text.length());
            }

            line++;
            // 跳过换行符
            if (pos < commit_message.length() && commit_message[pos] == '\n')
            {
                pos++;
            }
        }

        // 用空格填充剩余的行
        for (; line < input_lines; line++)
        {
            mvwhline(win, input_start_y + line, input_start_x, ' ', text_width);
        }

        // 如果当前没有输入焦点，反色显示"Commit"按钮
        if (!has_input_focus)
            wattron(win, A_REVERSE);
        mvwprintw(win, win_height - 2, win_width - 11, "[ Commit ]");
        if (!has_input_focus)
            wattroff(win, A_REVERSE);

        // 如果有输入焦点，计算并设置光标位置
        if (has_input_focus)
        {
            int cursor_y, cursor_x;
            calculate_cursor_position(cursor_y, cursor_x);
            wmove(win, cursor_y, cursor_x);
        }
    }

    // 刷新窗口显示
    wrefresh(win);
}



// GitInfo类的reinitialize方法实现
// 作用：重新初始化Git信息对象的状态
// 参数：new_dir - 新的工作目录路径
void GitInfo::reinitialize(const std::string &new_dir)
{
    // 更新当前工作目录为传入的新目录路径
    current_dir = new_dir;
    
    // 重置滚动位置为0，通常用于UI显示时的滚动条位置
    scroll_pos = 0;
    
    // 设置不显示提交输入框的标志位
    show_commit_input = false;
    
    // 清空之前存储的提交消息内容
    commit_message.clear();
    
    // 重置光标位置为0，通常用于文本输入时的光标位置
    cursor_pos = 0;
    
    // 设置输入框有焦点标志位为true，表示当前输入框处于活跃状态
    has_input_focus = true;
    
    // 调用read_directory方法重新读取当前目录下的Git信息
    read_directory();
}

std::string GitInfo::run()
{
    // 绘制窗口界面
    draw_window();

    // 定义变量ch用于存储用户输入的字符
    int ch;
    // 进入无限循环，等待用户输入
    while (true)
    {
        // 每次循环都重新绘制窗口，确保界面最新
        draw_window();
        // 从窗口中获取用户输入的字符
        ch = wgetch(win);

        // 如果输入的是'q'且当前没有输入焦点，则返回空字符串(退出)
        if (ch == 'q' && has_input_focus == false)
        {
            return "";
        }

        // 检查是否显示提交信息输入框
        if (show_commit_input)
        {
            // 如果当前有输入焦点(正在编辑提交信息)
            if (has_input_focus)
            {
                // 根据不同的按键进行不同的处理
                switch (ch)
                {
                case KEY_ENTER:  // 回车键
                case '\n':      // 换行符
                    // 在光标位置插入换行符
                    commit_message.insert(cursor_pos, 1, '\n');
                    // 光标位置后移
                    cursor_pos++;
                    break;
                case KEY_LEFT:  // 左箭头键
                    // 如果光标不在最左边，则左移光标
                    if (cursor_pos > 0)
                        cursor_pos--;
                    break;
                case KEY_RIGHT: // 右箭头键
                    // 如果光标不在字符串末尾，则右移光标
                    if (cursor_pos < static_cast<int>(commit_message.length()))
                        cursor_pos++;
                    break;
                case KEY_UP:    // 上箭头键
                {
                    // 计算文本宽度(窗口宽度减去边框)
                    int text_width = getmaxx(win) - 4;
                    // 找到当前行的起始位置
                    int line_start = cursor_pos;
                    while (line_start > 0 && commit_message[line_start - 1] != '\n')
                    {
                        line_start--;
                    }

                    // 如果不是第一行
                    if (line_start > 0)
                    {
                        // 光标移动到上一行末尾
                        cursor_pos = line_start - 1;
                        // 找到上一行的起始位置
                        int prev_line_start = cursor_pos;
                        while (prev_line_start > 0 && commit_message[prev_line_start - 1] != '\n')
                        {
                            prev_line_start--;
                        }
                        // 计算上一行的偏移量
                        int offset = cursor_pos - prev_line_start;
                        // 光标移动到上一行对应位置
                        cursor_pos = std::min(prev_line_start + offset, prev_line_start + text_width - 1);
                    }
                    break;
                }
                case KEY_DOWN:  // 下箭头键
                {
                    // 计算文本宽度
                    int text_width = getmaxx(win) - 4;
                    // 找到当前行的起始位置
                    int line_start = cursor_pos;
                    while (line_start > 0 && commit_message[line_start - 1] != '\n')
                    {
                        line_start--;
                    }

                    // 找到当前行的结束位置
                    int line_end = cursor_pos;
                    while (line_end < commit_message.length() && commit_message[line_end] != '\n')
                    {
                        line_end++;
                    }

                    // 如果不是最后一行
                    if (line_end < commit_message.length())
                    {
                        // 下一行的起始位置
                        int next_line_start = line_end + 1;
                        // 计算当前行的偏移量
                        int offset = cursor_pos - line_start;
                        // 光标移动到下一行对应位置
                        cursor_pos = std::min(next_line_start + offset, next_line_start + text_width - 1);
                        // 如果超出字符串长度，则移动到末尾
                        if (cursor_pos > commit_message.length())
                        {
                            cursor_pos = commit_message.length();
                        }
                    }
                    break;
                }
                case KEY_HOME:  // Home键
                {
                    // 找到当前行的起始位置
                    int line_start = cursor_pos;
                    while (line_start > 0 && commit_message[line_start - 1] != '\n')
                    {
                        line_start--;
                    }
                    // 光标移动到行首
                    cursor_pos = line_start;
                    break;
                }
                case KEY_END:   // End键
                {
                    // 找到当前行的结束位置
                    int line_end = cursor_pos;
                    while (line_end < commit_message.length() && commit_message[line_end] != '\n')
                    {
                        line_end++;
                    }
                    // 光标移动到行尾
                    cursor_pos = line_end;
                    break;
                }
                case '\t':      // Tab键
                    // 失去输入焦点
                    has_input_focus = false;
                    break;
                case KEY_BACKSPACE:  // 退格键
                case 127:            // Delete键(某些终端)
                    // 如果光标不在最左边，删除前一个字符
                    if (cursor_pos > 0)
                    {
                        commit_message.erase(cursor_pos - 1, 1);
                        cursor_pos--;
                    }
                    break;
                case KEY_DC:  // Delete键
                    // 如果光标不在字符串末尾，删除当前字符
                    if (cursor_pos < static_cast<int>(commit_message.length()))
                    {
                        commit_message.erase(cursor_pos, 1);
                    }
                    break;
                default:
                    // 如果是可打印字符，插入到光标位置
                    if (isprint(ch))
                    {
                        commit_message.insert(cursor_pos, 1, ch);
                        cursor_pos++;
                    }
                    break;
                }
                curs_set(0);
            }
            else  // 如果没有输入焦点
            {
                switch (ch)
                {
                case '\t':  // Tab键
                    // 获取输入焦点
                    has_input_focus = true;
                    break;
                case KEY_ENTER:  // 回车键
                case '\n':       // 换行符
                    // 如果提交信息不为空，则返回提交信息
                    if (!commit_message.empty())
                        return commit_message;
                    break;
                }
                curs_set(2);
            }
        }
        else  // 如果不显示提交信息输入框
        {
            switch (ch)
            {
            case KEY_UP:  // 上箭头键
                // 如果滚动位置大于0，向上滚动
                if (scroll_pos > 0)
                    scroll_pos--;
                break;
            case KEY_DOWN:  // 下箭头键
                // 如果滚动位置未到底部，向下滚动
                if (scroll_pos + (getmaxy(win) - 5) < static_cast<int>(items.size()))
                    scroll_pos++;
                break;
            case '\t':  // Tab键
                // 切换输入焦点状态
                has_input_focus = !has_input_focus;
                break;
            case KEY_ENTER:  // 回车键
            case '\n':       // 换行符
                // 如果没有输入焦点
                if (!has_input_focus)
                {
                    // 显示提交信息输入框
                    show_commit_input = true;
                    // 获取输入焦点
                    has_input_focus = true;
                    // 光标移动到提交信息末尾
                    cursor_pos = commit_message.length();
                }
                break;
            }
            curs_set(2);
        }
    }
}
